; DUT - Disk Utility for TurboDOS
; 11/29/85 by ESKAY
; All Lefts Reversed
;
cr	equ	0dh
lf	equ	0ah
;
cfunc	equ	5
tfunc	equ	50h
;
.z80
.request syslib
;
	cseg
;
start:	jr	begin
;
cls:	db	1bh,'E',0,0,0,0		; clear screen
curpos:	db	1bh,'F',0,0,0,0		; cursor pos
xory:	db	0			; 0=xy, nz=yx
coffs:	db	32			; cursor offset
;
begin:	ld	sp,stack		; set up local stack
	ld	c,25
	call	bdos##
	ld	(curdsk),a
	call	clrscr			; clear the screen
	ld	bc,9*256+0		; line 9 col 0
	call	gotoxy
	call	print##			; display banner
	cr,lf
	9,9,'+--------------------------------------------+',cr,lf
	9,9,'| DUT vers 1.50     11/29/85      (c)  ESKAY |',cr,lf
	9,9,'| Disk editor for TurboDOS - Type H for HELP |',cr,lf
	9,9,'+--------------------------------------------+',cr,lf,lf,0
	ld	c,12			; get priv level
	call	tfunc
	bit	7,b
	jr	nz,.isp			; continue if privileged
	call	print##
	cr,lf,7
	'ERROR: non-privileged logon',cr,lf,0
	di
	halt				; crash slave on the poor sucker
;
.isp:	call	opnnew			; open new $.DSK
loop:	call	disdrv			; display drive
	ld	bc,22*256+27		; line 22 column 27
	call	gotoxy
	call	print##
	'Enter command (H=HELP)  [_]',8,8,0
	call	capin##			; get command character
	call	cout##			; echo it
	call	clrerr			; clear error line
	call	getca			; get command address
	jr	nz,.bcmd		; skip if bad command
	jp	(hl)
;
.bcmd:	ld	bc,23*256+29
	call	gotoxy
	call	print##
	'Invalid command - H=HELP',7,0
	jr	loop
;
; read next record
;
nxtrec:	call	incgrp			; increment group
	jp	.ggrp.
;
; read previous record
;
prvrec:	call	decgrp
	jp	.ggrp.
;
incgrp:	ld	hl,(curgrp)
	ld	a,(grpsz)
	ld	b,a
	ld	a,(cursec)
	inc	a
	cp	b
	jr	nz,.igrn.
	ld	de,(maxgrp)
	call	comphd##
	inc	hl
	jr	nz,..ngrz
	ld	hl,0
..ngrz:	xor	a
	ld	(curgrp),hl
.igrn.:	ld	(cursec),a
	ret
;
decgrp:	ld	hl,(curgrp)
	ld	a,(cursec)
	dec	a
	jp	p,.dgrn.
	ld	a,h
	or	l
	dec	hl
	jr	nz,.dgr1.
	ld	hl,(maxgrp)
.dgr1.:	ld	(curgrp),hl
	ld	a,(grpsz)
	dec	a
.dgrn.:	ld	(cursec),a
	ret	
;
; help
;
help:	call	clrscr
	call	print##
	'DUT vers 1.40 (c) 1985 ESKAY',cr,lf,lf
	'Command list (see manual for details):',cr,lf,lf,lf
	9,'D  - (re)display current buffer',cr,lf
	9,'F  - Find the start of a file',cr,lf
	9,'G  - Goto group address',cr,lf
	9,'>  - next group (also .)',cr,lf
	9,'<  - previous group (also ,)',cr,lf
	9,'Q  - exit to TurboDOS',cr,lf
	9,'+  - increment current sector and display (also =)',cr,lf
	9,'-  - decrement current sector and display',cr,lf
	9,'!  - step to first data group',cr,lf
	9,'? or H = this list',cr,lf,0
	ld	bc,22*256+27
	call	gotoxy
	call	print##
	'Press any key to continue',0
	call	cin##
	call	clrscr
	jp	loop
;
; go to group
;
.gthi:	ld	bc,23*256+20
	call	gotoxy
	call	print##
	'ERROR: too high!',7,0
;
goto:	ld	bc,22*256+10		; row 16 col 10
	call	gotoxy
	call	print##
	'Enter GROUP NUMBER (0000..',0
	ld	hl,(maxgrp)
	dec	hl
	call	phl4hc##
	call	print##
	') in hex : [____]',8,8,8,8,8,0
	call	bbline##
	or	a
	jr	z,.gotx.		; aborted
	call	eval16##
	ld	hl,(maxgrp)
	call	comphd##		; too high?
	jr	c,.gthi			; too high!
grpntr:	ld	(curgrp),de
	ex	de,hl
	xor	a
	ld	(cursec),a
ggdis1:	call	clcgrp
	ld	(dskfcb+35),a
	ld	(dskfcb+33),hl
.ggdis:	ld	de,dskfcb
	ld	c,33
	call	bdos##
result::nop				; check rr result
	jp	disbuf
;
.gotx.:	ld	bc,22*256+0
	call	gotoxy
	call	clreol
	call	clrerr
	jp	loop
;
; find a file
;
filfnd:	ld	bc,22*256+20
	call	gotoxy
	call	print##
	'Enter filename to be found : ',0
	ld	a,1
	call	bbline##
	or	a
	jr	z,.filx.		; abort
	ld	de,5ch			; point to fcb area
	call	fname##			; parse file name
	ld	a,c			; get specified user
	cp	0ffh			; none?
	jr	z,..nus.		;   skip if no user
	ld	e,c
	ld	c,32
	call	bdos##
..nus.:	ld	de,fndbuf		; set up temporary dma
	ld	c,26
	call	bdos##
	ld	de,5ch
	ld	c,17			; search first
	call	bdos##
	push	af
	ld	de,80h
	ld	c,26
	call	bdos##
	pop	af
	inc	a
	jr	nz,..ff..		; found file
	call	print##
	'FILE NOT FOUND - press any key       ',7,0
	call	cin##
	jr	.filx.
;
..ff..:	ld	de,fndbuf
	ld	hl,-16
	ld	bc,32
..fl..:	add	hl,bc
	dec	a
	jr	nz,..fl..
	add	hl,de
	ld	e,(hl)
	inc	hl
	ld	d,(hl)
	jp	grpntr
;
.filx.:	ld	bc,22*256+0
	call	gotoxy
	call	clreol
	jp	loop
;
; go to next group
;
nxtgrp:	ld	hl,(curgrp)
	inc	hl
	ld	(curgrp),hl
	push	hl
	dec	hl
	ld	de,(maxgrp)
	or	a
	sbc	hl,de
	ld	a,h
	or	l
	pop	hl
	jr	nz,.ggrp.
	ld	hl,0
	ld	(curgrp),hl
.ggrp.::	call	clcgrp			; calc sector from group
	ld	(dskfcb+35),a
	ld	b,a
	ld	a,(cursec)
	ld	e,a
	ld	d,0
	add	hl,de
	ld	(dskfcb+33),hl
	ld	a,b
	jr	nc,..novf
	inc	a
..novf:	ld	(dskfcb+35),a
	jp	.ggdis			; set file pointer and display
;
prvgrp:	ld	hl,(curgrp)
	ld	a,h
	or	l
	jr	nz,.pg.
	ld	hl,(maxgrp)
	jr	.pg1.
;
.pg.:	dec	hl
.pg1.:	ld	(curgrp),hl
	jr	.ggrp.
;
; step to first data group
;
frstdt:	ld	hl,(dirsiz)
	ld	(curgrp),hl
	xor	a
	ld	(cursec),a
	ld	(dskfcb+35),a
	call	clcgrp
	ld	(dskfcb+33),hl
	jp	.ggdis
;
; edit command
;
edit:	ld	a,(dison)
	dec	a
	jr	z,.eact.
	ld	a,7
	call	cout##
	jp	loop
;
.eact.:	ld	bc,3*256+6		; beginning of field
	ld	(editpt),bc
edlp:	ld	bc,(editpt)
	call	gotoxy
	call	capin##
	cp	3			; exit
	jp	z,loop
	cp	'E'-40h			; ^E = up
	jr	z,.edup.
	cp	'X'-40h			; ^X = down
	jr	z,.eddn.
	cp	'S'-40h			; ^S = left
	jr	z,.edlf.
	cp	'D'-40h			; ^D = right
	jr	z,.edrt.
	cp	27h			; check '
	jp	z,.edasc		; edit ascii
	call	nybble			; see if nybble
	jp	nc,.edda.		; it is, edit data
	ld	a,7
	call	cout##
	jr	edlp
;
.edup.:	ld	a,(editpt+1)		; get row
	dec	a
	cp	2
	jr	nz,.esnt.
	ld	a,10			; set bottom
.esnt.:	ld	(editpt+1),a
	jr	edlp
;
.eddn.:	ld	a,(editpt+1)
	inc	a
	cp	11
	jr	nz,.esnt.
	ld	a,3
	jr	.esnt.
;
.edlf.:	ld	a,(editpt)
	sub	3
	cp	3
	jr	nz,.eslt.
	ld	a,51
.eslt.:	ld	(editpt),a
	jp	edlp
;
.edrt.:	ld	a,(editpt)
	add	a,3
	cp	54
	jr	nz,.eslt.
	ld	a,6
	jr	.eslt.
;
.edasc:	call	cin##			; get ascii character
	call	pa2hc##
	jr	..asci
;
.edda.:	call	hexo			; display high nybble
	cp	'A'
	jr	c,...x
	sub	7
...x:	and	0fh
	rla
	rla
	rla
	rla
	ld	b,a			; save it
.ede..:	call	capin##
	call	nybble
	jr	c,.ede..
	call	hexo
	cp	'A'
	jr	c,...y
	sub	7
...y:	and	0fh
	or	b			; a has byte now
..asci:	push	af
	call	calcbp			; calculate buffer pointer
	pop	af
	ld	(hl),a
	ld	a,l
	and	0fh
	add	a,55
	ld	bc,(editpt)
	ld	c,a
	call	gotoxy
	ld	a,(hl)
	and	7fh
	cp	7fh
	jr	z,.ed...
	cp	' '
	jr	nc,.edn..
	cp	7fh
.ed...:	ld	a,'.'
.edn..:	call	cout##
	jp	.edrt.			; go to next hex on same line
;
; write buffer
;
write:	ld	de,dskfcb
	ld	c,34
	call	bdos##
	jp	loop
;
; advance and display directory buffer
;
advdir:




;
; display buffer command
;
disbuf:	call	clrscr
	ld	a,1
	ld	(dison),a
	call	print##
	'RELATIVE SECTOR : ',0
	ld	hl,(dskfcb+33)
	inc	hl
	ld	a,(dskfcb+35)
	ld	e,a
	call	p24b
	ld	bc,0*256+25
	call	gotoxy
	call	print##
	' OF ',0
	ld	hl,(maxrec)
	inc	hl
	ld	a,(maxrec+2)
	ld	e,a
	call	p24b
	ld	bc,0*256+38
	call	gotoxy
	call	print##
	'GROUP:SECTOR : ',0
	ld	hl,(curgrp)
	call	phl4hc##
	ld	a,':'
	call	cout##
	ld	a,(cursec)
	call	pa2hc##
	call	disdrv			; display drive
	call	dispbf			; display buffer contents
	jp	loop			; and go back for next command
;
quit:	call	clrscr
	ld	bc,23*256+0
	call	gotoxy
	rst	0
;
; subroutines
;
; calculate buffer from screen address
;
calcbp::
	ld	hl,(editpt)
	ld	a,l			; get column
	ld	b,0
	sub	6
.div3.:	inc	b
	sub	3
	jr	nc,.div3.
	ld	l,b
	ld	a,h
	ld	h,0
	sub	3
	rla
	rla
	rla
	rla
	add	a,7fh
	add	a,l
	ld	l,a
	ret
;
; test a if valid nybble
;
nybble:	cp	'0'
	ret	c
	cp	'F'+1
	ccf
	ret	c
	cp	'9'+1
	ccf
	ret	nc
	sub	7
	cp	'9'+1
	ret
;
; hex out
;
hexo:	cp	'9'+1
	jp	c,cout##
	add	a,7
	jp	cout##
;
; calc sector from group
;
clcgrp::
	ld	a,(grpmul)
	ld	b,a
	xor	a
.ggrm.:	rl	l
	rl	h
	rla
	djnz	.ggrm.
	ret
;
; open $.DSK, $.DIR
;
opnnew:	ld	de,dskfcb
	call	initfcb##
	ld	c,35			; get file size
	call	bdos##
	ld	hl,(dskfcb+33)
	ld	a,(dskfcb+35)
	dec	hl
	ld	(maxrec),hl
	ld	(maxrec+2),a
	call	f$open##
	ld	de,80h
	ld	c,26
	call	bdos##
	ld	de,dskfcb
	ld	hl,0
	ld	(dskfcb+32),hl
	ld	(dskfcb+34),hl
	call	f$read##
	ld	c,19
	ld	a,(curdsk)
	ld	e,a
	call	tfunc
	dec	hl
	ld	(maxgrp),hl
	and	0fh
	ld	(grpmul),a
	sub	2
	ld	b,a
	ld	a,4
.sla.:	or	a
	rla
	djnz	.sla.
	ld	(grpsz),a
	ld	a,c
	ld	(dirsiz),a
	ret
;
; display drive
;
disdrv:	ld	bc,0*256+70
	call	gotoxy
	call	print##
	'DRIVE ',0
	ld	a,(curdsk)
	add	a,'A'
	call	cout##
	ld	a,':'
	jp	cout##
;
; display buffer on screen (80H)
;
dispbf:	ld	de,0
	ld	bc,3*256+0		; row 3 col 0
disobf:	call	gotoxy
	add	hl,de
	ld	hl,80h			; point to buffer
.disl.:	push	hl			; save hl
	ld	b,16			; display 16 hex bytes
	push	hl
	res	7,l
	ld	h,0
	call	phl4hc##		; display relative address
	pop	hl
	call	space2			; 2 spaces
.h16l.:	ld	a,(hl)			; get byte
	call	pa2hc##			; print hex
	call	space
	inc	hl
	djnz	.h16l.			; complete 16 bytes
	pop	hl			; get addr back
	ld	b,16			; 16 bytes again
	call	space
.a16l.:	ld	a,(hl)			; get byte
	and	7fh			; strip hi bit
	cp	7fh
	jr	z,.a16..
	cp	' '			; dot if control
	jr	nc,.a16n.
.a16..:	ld	a,'.'
.a16n.:	call	cout##
	inc	hl
	djnz	.a16l.			; loop until done
	call	crlf##			; new line
	ld	a,l			; get low byte of buffer addr
	or	a			; zero?
	ret	z			;   yes, done
	jr	.disl.			;     else do next line
;
; get command address or NZ
;
getca:	ld	hl,cmdtbl
	ld	b,a
.gca.:	ld	a,(hl)
	or	a
	jr	z,.gcx.
	cp	b
	jr	z,.gcg.
	inc	hl
	inc	hl
	inc	hl
	jr	.gca.
;
.gcg.:	inc	hl
	ld	a,(hl)
	inc	hl
	ld	h,(hl)
	ld	l,a
	xor	a
	ret
;
.gcx.:	dec	a
	ret
;
clrerr:	push	af
	push	bc
	ld	bc,23*256+0		; row 23 col 0
	call	gotoxy
	pop	bc
	pop	af
;
clreol:	push	af
	push	bc
	ld	b,79
	ld	a,' '
.clel.:	call	cout##
	djnz	.clel.
	pop	bc
	pop	af
	ret
;
space2:	call	space
space:	ld	a,' '
	jp	cout##
;
; position cursor. b=x, c=y
;
gotoxy:	push	hl
	ld	hl,curpos
	call	pstrg
	ld	a,(xory)		; check xy or yx
	or	a			; if zero, then xy
	jr	z,..xy
	ld	a,b
	ld	b,c
	ld	c,a			; exchange b and c
..xy:	ld	a,(coffs)		; get cursor offset
	push	af			; save for next
	add	a,b
	call	cout##
	pop	af
	add	a,c
	call	cout##
	pop	hl
	ret
;
clrscr:	push	hl
	ld	hl,cls
	call	pstrg
	pop	hl
	ret
;
pstrg:	ld	a,(hl)
	or	a
	ret	z
	call	cout##
	inc	hl
	jr	pstrg
;
p24b:	ld	ix,n24b
	push	ix
	ld	b,12
	xor	a
.p24l:	ld	(ix+0),a
	inc	ix
	djnz	.p24l
	pop	ix
	ld	a,3
	call	hxdc24##
	ld	hl,n24b
	jp	pstr##
;
	dseg
;
; command table
;
cmdtbl:	db	'Q'			; exit
	dw	quit
	db	'D'			; display buffer
	dw	disbuf
	db	'+'			; next record
	dw	nxtrec
	db	'='
	dw	nxtrec
	db	'-'			; previous record
	dw	prvrec
	db	'H'
	dw	help
	db	'?'
	dw	help
	db	'G'			; go to group
	dw	goto
	db	'F'			; find file
	dw	filfnd
	db	'<'			; previous group
	dw	prvgrp
	db	'>'			; next group
	dw	nxtgrp
	db	','
	dw	prvgrp
	db	'.'
	dw	nxtgrp
	db	'!'			; first data grp
	dw	frstdt
	db	'E'			; edit
	dw	edit
	db	'W'			; write
	dw	write
	db	0
;
curdsk:	db	0
dirsiz:	dw	0
maxrec:	ds	3
curgrp:	dw	0
cursec:	db	0
maxgrp:	dw	0
grpsz:	db	0
grpmul:	db	0
;
n24b:	db	0,0,0,0,0,0,0,0,0,0,0,0
;
dison:	db	0
editpt:	dw	0
;
dskfcb:	db	0,'$       DSK',0,0,0,0
	ds	24
;
fndbuf:	ds	128
	ds	100
stack	equ	$
	end
ld	a,(hl)
	or	a
	ret	z
	call	cout##
	inc	hl
	jr	pstrg
;
p24b:	ld	ix,n24b
	push	ix
	ld	b,12
	xor	a
.p24l:	ld	(ix+0),